<?php

/**
 * AfroSoft ACL API
 *
 * @package API
 *
 * @author AfroSoft
 * @version @build.version@ (Build @build.number@)
 * @license http://www.mozilla.org/MPL/MPL-1.1.txt MPL 1.1
 * @copyright Copyright (c) AfroSoft
 */

/* * **** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the AfroSoft ACL API.
 *
 * The Initial Developer of the Original Code is AfroSoft.
 * Portions created by the Initial Developer are Copyright (C) 2011 the Initial
 * Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Xavier L. <xavier.l@afrosoft.tk>
 *
 * ***** END LICENSE BLOCK ***** */

require_once 'ACL.php';
require_once 'Exception.php';

/**
 * Every function in this API does a permission check on the user performing 
 * the administrative action prior to completing the task. This is to make sure 
 * that the user performing the actions has sufficient priviledges and could 
 * help prevent disasters if an admin page is left unsecured.
 */
class ACLAdmin {

    /**
     * The variable holding the database layer object.
     */
    public static $dbi;

    /**
     * The addItem function adds an item to the database.
     * 
     * The parameters used with this function are:
     * - admin : the ID of the user performing the administrative task
     * - key : the key of the item to add
     * - name : the human-readable name for the item to add
     * - desc : the description of the item to add
     * - default : the default permission to set, in a form of a boolean
     * (false = deny)
     *
     * @param array $param
     * @return bool
     */
    public static function addItem(array $param) {
        if (empty(self::$dbi)) {
            throw new ACLDatabaseNotConnectedException;
        }

        $param = array_merge(array(
            'admin' => null,
            'key' => null,
            'name' => null,
            'desc' => null,
            'default' => null,
                ), $param);

        if (empty($param['admin']) || empty($param['key']) || empty($param['name']) || empty($param['desc']) || !isset($param['default'])) {
            throw new MissingRequiredArgumentException(array('admin', 'key' , 'name', 'desc', 'default'));
        }
        
        $permissionCheck = ACLRequest::checkPermission(array('key' => 'admin-acl-item-add', 'user' => $param['admin'], 'explicit' => true));
        if (($permissionCheck == ACLPermission::UNKNOWN) || (($permissionCheck & ACLPermission::ALLOW) == 0)) {
            throw new InsufficientPermissionsException('admin-acl-item-add', $param['admin']);
        }
        
        unset($param['admin']);

        try {
            return self::$dbi->addItem($param);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This function edits an existing item in the database. The function will 
     * complain and throw an exception if the item is not already defined.
     * 
     * The parameters used with this function are:
     * - admin : the ID of the user performing the administrative task
     * - key : the item key for which the record should be modified
     * - new_key : the modification of the key
     * - new_name : the modification of the name
     * - new_desc : the modification of the description
     * - new_default :  the modification of the default permission
     *
     * Only one of the parameters with the 'new_' prefix is required, but they 
     * can be all specified in one operation.
     * 
     * @param array $param
     * @return bool
     */
    public static function editItem(array $param) {
        if (empty(self::$dbi)) {
            throw new ACLDatabaseNotConnectedException;
        }

        $param = array_merge(array(
            'admin' => null,
            'key' => null,
            'new_key' => null,
            'new_name' => null,
            'new_desc' => null,
            'new_default' => null,
                ), $param);

        if (empty($param['admin']) || empty($param['key'])) {
            throw new MissingRequiredArgumentException(array('admin', 'key'));
        }
        if (empty($param['new_key']) && empty($param['new_name']) && empty($param['new_desc']) && !isset($param['new_default'])) {
            throw new MissingListRequiredArgumentException(array('new_key', 'new_name', 'new_desc', 'new_default'));
        }

        $permissionCheck = ACLRequest::checkPermission(array('key' => 'admin-acl-item-edit', 'user' => $param['admin'], 'explicit' => true));
        if (($permissionCheck == ACLPermission::UNKNOWN) || (($permissionCheck & ACLPermission::ALLOW) == 0)) {
            throw new InsufficientPermissionsException('admin-acl-item-edit', $param['admin']);
        }
        
        unset($param['admin']);
        
        try {
            return self::$dbi->editItem($param);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * The deleteItem function removes an item from the database. It does not 
     * revoke individual permissions granted to users or groups for that item. 
     * The database may do it automatically if foreign keys are set up.
     * 
     * The parameters used by this function are:
     * - key : the key for which the record should be removed.
     * 
     * @param array $param
     * @return bool
     */
    public static function deleteItem(array $param) {
        if (empty(self::$dbi)) {
            throw new ACLDatabaseNotConnectedException;
        }

        $param = array_merge(array(
            'admin' => null,
            'key' => null,
                ), $param);

        if (empty($param['admin']) || empty($param['key'])) {
            throw new MissingRequiredArgumentException(array('admin', 'key'));
        }
        $permissionCheck = ACLRequest::checkPermission(array('key' => 'admin-acl-item-delete', 'user' => $param['admin'], 'explicit' => true));
        if (($permissionCheck == ACLPermission::UNKNOWN) || (($permissionCheck & ACLPermission::ALLOW) == 0)) {
            throw new InsufficientPermissionsException('admin-acl-item-delete', $param['admin']);
        }
        
        unset($param['admin']);

        try {
            return self::$dbi->removeItem($param);
        } catch (Exception $e) {
            throw $e;
        }
    }

    public static function givePermission($param) {
        if (empty(self::$dbi)) {
            throw new ACLDatabaseNotConnectedException;
        }
        $param = array_merge(array(
            'admin' => null,
            'key' => null,
            'id' => null,
            'type' => null,
            'permission' => null,
                ), $param);

        if (empty($param['admin']) || empty($param['key']) || empty($param['id']) || empty($param['type']) || !isset($param['permission'])) {
            throw new MissingRequiredArgumentException(array('admin', 'key', 'id', 'type', 'permission'));
        }
        $permissionCheck = ACLRequest::checkPermission(array('key' => 'admin-acl-permission-give', 'user' => $param['admin'], 'explicit' => true));
        if (($permissionCheck == ACLPermission::UNKNOWN) || (($permissionCheck & ACLPermission::ALLOW) == 0)) {
            throw new InsufficientPermissionsException('admin-acl-permission-give', $param['admin']);
        }
        
        unset($param['admin']);
        
        try {
            return self::$dbi->addPermission($param);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This function edits an existing permission. The function will 
     * complain and throw an exception if the permission is not already defined.
     * 
     * The parameters used with this function are:
     * - admin : the ID of the user performing the administrative task
     * - key : the item key for which the record should be modified
     * - id : the id of the user or group for which the permission is modified
     * - type : either 'user' or 'group'
     * - new_key : the modification of the item key
     * - new_id : the modification of the id
     * - new_permission :  the modification of the permission
     *
     * Only one of the parameters with the 'new_' prefix is required, but they 
     * can be all specified in one operation.
     * 
     * @param array $param
     * @return bool
     */
    public static function editPermission(array $param) {
        if (empty(self::$dbi)) {
            throw new ACLDatabaseNotConnectedException;
        }

        $param = array_merge(array(
            'admin' => null,
            'key' => null,
            'id'  => null,
            'type' => null,
            'new_key' => null,
            'new_id' => null,
            'new_permission' => null,
                ), $param);

        if (empty($param['admin']) || empty($param['key']) || empty($param['id']) || empty($param['type'])) {
            throw new MissingRequiredArgumentException(array('admin', 'key', 'id', 'type'));
        }
        if (empty($param['new_key']) && empty($param['new_id']) && !isset($param['new_permission'])) {
            throw new MissingListRequiredArgumentException(array('new_key', 'new_id', 'new_permission'));
        }

        $permissionCheck = ACLRequest::checkPermission(array('key' => 'admin-acl-permission-edit', 'user' => $param['admin'], 'explicit' => true));
        if (($permissionCheck == ACLPermission::UNKNOWN) || (($permissionCheck & ACLPermission::ALLOW) == 0)) {
            throw new InsufficientPermissionsException('admin-acl-permission-edit', $param['admin']);
        }
        
        unset($param['admin']);
        
        try {
            return self::$dbi->editPermission($param);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * This function deletes a permission from the database.
     * 
     * The parameters used with this function are:
     * - admin : the ID of the user performing the administrative task
     * - id : the ID of the user or group from which to remove the permission
     * - type : either 'user' or 'group'
     * - key : the key associated with permission to remove
     * 
     * @param array $param
     * @return bool 
     */
    public static function revertPermission($param) {
        if (empty(self::$dbi)) {
            throw new ACLDatabaseNotConnectedException;
        }

        $param = array_merge(array(
            'admin' => null,
            'key' => null,
            'id' => null,
            'type' => null,
                ), $param);

        if (empty($param['admin']) || empty($param['key']) || empty($param['id']) || empty($param['type'])) {
            throw new MissingRequiredArgumentException(array('admin', 'key', 'id', 'type'));
        }
        $permissionCheck = ACLRequest::checkPermission(array('key' => 'admin-acl-permission-revert', 'user' => $param['admin'], 'explicit' => true));
        if (($permissionCheck == ACLPermission::UNKNOWN) || (($permissionCheck & ACLPermission::ALLOW) == 0)) {
            throw new InsufficientPermissionsException('admin-acl-permission-revert', $param['admin']);
        }
        
        unset($param['admin']);

        try {
            return self::$dbi->revertPermission($param);
        } catch (Exception $e) {
            throw $e;
        }
    }

}
